/*
 * Copyright 2013 Sigurd Randoll <srandoll@digiway.de>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.digiway.rapidbreeze.server.model.storage.provider;

import de.digiway.rapidbreeze.server.model.storage.AuthenticationStatus;
import de.digiway.rapidbreeze.server.model.storage.FileStatus;
import de.digiway.rapidbreeze.server.model.storage.StorageProviderDownloadClient;
import de.digiway.rapidbreeze.server.model.storage.UrlStatus;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.impl.cookie.BasicClientCookie;

/**
 * {@linkplain StorageProviderDownloadClient} implementation for the
 * One-Click-Hoster Share-Online at http://www.share-online.biz/. An example
 * link to a file hosted at Share-Online looks like this:
 * http://www.share-online.biz/dl/KJGPO5GMLZ
 *
 * @author Sigurd Randoll <srandoll@digiway.de>
 */
public class ShareOnlineBiz implements StorageProviderDownloadClient {

    private DefaultHttpClient httpClient;
    private String username = null;
    private String password = null;
    private AuthenticationStatus currentAuthenticationStatus;
    public static final String USER_ACCOUNT_QUERY = "http://api.share-online.biz/cgi-bin?q=userdetails&username=%s&password=%s";
    public static final String URL_STATUS_QUERY = "http://api.share-online.biz/linkcheck.php?links=%s";
    public static final String URL_DETERMINE_QUERY = "http://api.share-online.biz/cgi-bin?q=linkdata&username=%s&password=%s&lid=%s";

    public ShareOnlineBiz() {
        ClientConnectionManager connectionManager = new PoolingClientConnectionManager();
        this.httpClient = new DefaultHttpClient(connectionManager);
        this.currentAuthenticationStatus = AuthenticationStatus.NOT_AUTHENTICATED;
    }

    @Override
    public InputStream start(URL url, long offset) throws IOException {
        if (!currentAuthenticationStatus.equals(AuthenticationStatus.AUTHENTICATED)) {
            throw new IllegalStateException("The client hasn't been authenticated at the storage provider " + this + ". Free downloading isn't supported yet.");
        }
        if (!getUrlStatus(url).getFileStatus().equals(FileStatus.OK)) {
            throw new IllegalStateException("The file at the location " + url + " is not available.");
        }

        Properties props = new Properties();
        HttpResponse response;

        String query = String.format(URL_DETERMINE_QUERY, username, password, getLinkId(url));
        HttpGet httpGet = new HttpGet(query);

        try {
            response = httpClient.execute(httpGet);
            props.load(response.getEntity().getContent());
        } finally {
            httpGet.releaseConnection();
        }

        httpGet = new HttpGet(props.getProperty("URL"));
        if (offset > 0) {
            httpGet.addHeader("Range", "bytes=" + offset + "-");
        }
        response = httpClient.execute(httpGet);

        if (offset > 0 && response.getStatusLine().getStatusCode() != 206) {
            throw new IllegalArgumentException("The server does not support resuming. Returning status code:"
                    + response.getStatusLine().getStatusCode());
        }
        return response.getEntity().getContent();
    }

    @Override
    public void authenticate(String username, String password) {
        this.username = username;
        this.password = password;
        InputStream is = null;

        try {
            String accountInfoUrl = String.format(USER_ACCOUNT_QUERY, username, password);
            HttpGet get = new HttpGet(accountInfoUrl);
            HttpResponse resp = httpClient.execute(get);

            is = resp.getEntity().getContent();
            Properties props = new Properties();
            props.load(is);
            is.close();

            // Auth string
            String a = props.getProperty("a");
            if (a == null || a.isEmpty()) {
                throw new IOException("Cannot authenticate user '" + username + "' at storage provider " + toString());
            }

            BasicClientCookie authCookie = new BasicClientCookie("a", a);
            authCookie.setDomain("share-online.biz");
            httpClient.getCookieStore().addCookie(authCookie);
            currentAuthenticationStatus = AuthenticationStatus.AUTHENTICATED;
        } catch (IOException ex) {
            Logger.getLogger(ShareOnlineBiz.class
                    .getName()).log(Level.SEVERE, null, ex);
            currentAuthenticationStatus = AuthenticationStatus.AUTHENTICATION_FAILED;
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    private String getLinkId(URL url) {
        if (url == null) {
            throw new IllegalStateException("URL is not yet set.");
        }
        String path = url.getPath();
        String[] split = path.split("/dl/");
        if (split.length != 2) {
            throw new IllegalStateException("Cannot retrieve link id from response path:" + path);
        }
        return split[1];
    }

    @Override
    public UrlStatus getUrlStatus(final URL url) {
        if (url == null) {
            throw new IllegalStateException("URL has not yet been set.");
        }
        String linkId = getLinkId(url);
        String query = String.format(URL_STATUS_QUERY, linkId);

        try {
            HttpGet get = new HttpGet(query);
            HttpResponse resp = httpClient.execute(get);
            String content = IOUtils.toString(resp.getEntity().getContent());

            final String[] split = content.split(";");
            if (split.length != 3 && split.length != 4) {
                throw new IllegalStateException("API of " + getName() + " has been changed. Cannot determine link status.");
            }
            final boolean ok = split.length == 4 ? true : false;

            return new UrlStatus() {
                @Override
                public String getFilename() {
                    if (ok) {
                        return split[2].trim();
                    }
                    return "";
                }

                @Override
                public Long getFileSize() {
                    if (ok) {
                        return Long.parseLong(split[3].trim());
                    }
                    return 0L;
                }

                @Override
                public FileStatus getFileStatus() {
                    if (ok && "OK".equals(split[1])) {
                        return FileStatus.OK;
                    }
                    return FileStatus.NOT_AVAILABLE;
                }

                @Override
                public String getMd5() {
                    return null;
                    // TODO:
//                    throw new UnsupportedOperationException("Not supported yet.");
                }

                @Override
                public String getUrl() {
                    return url.toString();
                }
            };
        } catch (IOException | RuntimeException ex) {
            Logger.getLogger(ShareOnlineBiz.class.getName()).log(Level.SEVERE, null, ex);
            return new UrlStatus() {
                @Override
                public String getFilename() {
                    return "";
                }

                @Override
                public Long getFileSize() {
                    return 0L;
                }

                @Override
                public FileStatus getFileStatus() {
                    return FileStatus.NOT_AVAILABLE;
                }

                @Override
                public String getMd5() {
                    return null;
                }

                @Override
                public String getUrl() {
                    return url.toString();
                }
            };
        }
    }

    @Override
    public AuthenticationStatus getAuthenticationStatus() {
        return currentAuthenticationStatus;
    }

    @Override
    public String getName() {
        return "Share-Online";
    }

    @Override
    public String toString() {
        return getName();
    }

    @Override
    public boolean canHandle(URL url) {
        String host = url.getHost().toLowerCase();
        if (host.startsWith("www.share-online.biz") || host.startsWith("share-online.biz")) {
            return true;
        }
        return false;
    }
}
